package multimedia.model.coding;

import java.util.*;

public class HuffmanDecoder
{
    Hashtable<String, Character> huffDecodeTable = new Hashtable<String, Character>();
    String decodedData = "";
    String sDecodedData;
    String dData;

    Hashtable<Byte, String> decodingBitMap = new Hashtable<Byte, String>();
    ArrayList<Byte> bEncodedData;

    Hashtable<Character, String> huffEncodeTable;

    int dataLength;

    public String decode( ArrayList<Byte> bEncodedData, Hashtable<Character, String> huffEncodeTable, int dataLength )
    {

        this.bEncodedData = bEncodedData;
        this.huffEncodeTable = huffEncodeTable;
        this.dataLength = dataLength;

        buildDecodingBitMap();

        decodeToBitsAsString();

        buildHuffDecodeTable();

        decodeToCharacters();

        return decodedData.substring(0, dataLength);
    }

    void buildDecodingBitMap()
    {
        for ( int i = 0; i <= 255; i++ )
        {
            StringBuffer Buffer = new StringBuffer();

            if ( (i & 128) > 0 )
            {
                Buffer.append("1");
            } else
            {
                Buffer.append("0");
            }

            if ( (i & 64) > 0 )
            {
                Buffer.append("1");
            } else
            {
                Buffer.append("0");
            }

            if ( (i & 32) > 0 )
            {
                Buffer.append("1");
            } else
            {
                Buffer.append("0");
            }

            if ( (i & 16) > 0 )
            {
                Buffer.append("1");
            } else
            {
                Buffer.append("0");
            }

            if ( (i & 8) > 0 )
            {
                Buffer.append("1");
            } else
            {
                Buffer.append("0");
            }

            if ( (i & 4) > 0 )
            {
                Buffer.append("1");
            } else
            {
                Buffer.append("0");
            }

            if ( (i & 2) > 0 )
            {
                Buffer.append("1");
            } else
            {
                Buffer.append("0");
            }

            if ( (i & 1) > 0 )
            {
                Buffer.append("1");
            } else
            {
                Buffer.append("0");
            }

            decodingBitMap.put((byte) (i), Buffer.toString());
        }
    }

    void decodeToBitsAsString()
    {
        StringBuffer Buffer = new StringBuffer();

        for ( Byte element : bEncodedData )
        {
            byte wholeByte = element;
            Buffer.append(decodingBitMap.get(wholeByte));
        }// end for-each

        // Convert from StringBuffer to String
        sDecodedData = Buffer.toString();
    }

    void buildHuffDecodeTable()
    {
        Enumeration<Character> enumerator = huffEncodeTable.keys();
        while ( enumerator.hasMoreElements() )
        {
            Character nextKey = enumerator.nextElement();
            String nextString = huffEncodeTable.get(nextKey);
            huffDecodeTable.put(nextString, nextKey);
        }
    }

    void decodeToCharacters()
    {
        StringBuffer output = new StringBuffer();
        StringBuffer workBuf = new StringBuffer();

        for ( int i = 0; i < sDecodedData.length(); i++ )
        {
            workBuf.append(sDecodedData.charAt(i));
            if ( huffDecodeTable.containsKey(workBuf.toString()) )
            {
                output.append(huffDecodeTable.get(workBuf.toString()));
                workBuf = new StringBuffer();
            }
        }
        decodedData = output.toString();
        // dData = decodedData;
    }

    // public String getDecodedData()
    // {
    // return dData;
    // }
}